
// ReadPacketsLog.java
// Andrew Davison, ad@fivedots.coe.psu.ac.th, October 2012

/*  Read in the MindWave packet data stored in the text file
    supplied as the argument to this program, and print it out
    in a more high-level format.

    The ThinkGear Communications Driver (TGCD) packet format isn't 
    described anywhere in the developers download, although you
    can infer a lot by reading about the packet format used by 
    the ThinkGear Connector (TGC). The TGC is the other way to
    communicate with the MindWave: it creates a 
    local TCP socket link to a server connected to the MindWave.
    The relevant document is:
     \MindSet Development Tools\ThinkGear\mindset_communications_protocol.pdf

    Another source is the NeuroSky forum post:
       http://support.neurosky.com/discussions/misc/25-sample-log-file

    There's a little bit of information in the TGCD section of
    the developer's guide in the unzipped developers tools:
        \MindSet Development Tools\developer_tools_2.1_development_guide.pdf

*/

import java.io.*;
import java.util.*;


 
public class ReadPacketsLog 
{

  private static long startTime = 0;
  private static boolean showRaws = false;
      // if this is set to true then **lots** of raw data is printed


  public static void main(String[] args) 
  {
    if ((args.length < 1) || (args.length > 2)) {
      System.out.println("Usage: java ReadPacketsLog <log fnm> [raw]");
      System.exit(1);
    }

    if (args.length == 2) {
      if (args[1].toLowerCase().equals("raw")) {
        System.out.println("Reporting raw packets");
        showRaws = true;
      }
    }

    BufferedReader br = null;
    try {
      String line;
      System.out.println("Reading packets log from " + args[0]);
      br = new BufferedReader(new FileReader(args[0]));
      while ((line = br.readLine()) != null) {
        processLine(line);
        // System.out.println(line);
      }
    } 
    catch (IOException e) 
    {  System.out.println(e); } 
    finally {
      try {
        if (br != null)
          br.close();
      } 
      catch (IOException ex) 
      {  System.out.println(ex); }
    }
  }  // end of main()



  private static void processLine(String line)
  /* An example line has the format: 
        1350614425.491: [80]    119, 0077, -2.302053

     The line starts with the number of seconds since UNIX epoch time, 
     followed by millisecond precision after the "."

     Next up is a 2-digit hexadecimal code enclosed in square brackets,
     followed by a series of data items separated by commas.
  */
  {
    String[] lineData = line.split(":");
    if (lineData.length == 2) {
      // System.out.println(lineData[0] + " ==> " + lineData[1]);
      long eTime = elapsedTime(lineData[0]);
      printPacket(lineData[1].trim(), eTime);
    }
    else
      System.out.println("Line format incorrect: \"" + line + "\"");
  }  // end of processLine()




  private static long elapsedTime(String timeStr)
  /* Store the very first timestamp in startTime as a millisecond
     integer. Subsequent timestamps are substracted from this start time
     to get the elapsed time in milliseconds.
  */
  {
    long eTime = 0;
    try {
      long time = Long.parseLong(timeStr.replace(".", "")); 
      // remove the "."  in text such as "1350614425.491", and convert to long
      // System.out.println(time);
      if (startTime == 0)
        startTime = time;
      eTime = time-startTime;
      // System.out.print("Elapsed time (ms): " +  eTime);
    }
    catch (NumberFormatException e)
    {  System.out.println("Could not parse time: \"" + timeStr + "\"");  }
    return eTime;
  }  // end of elapsedTime()



  private static void printPacket(String dataStr, long eTime)
  /* The packet starts with a 2-digit hexadecimal code in
     square brackets. For example: 
             [80]    155, 009B, -2.090909
     or      [83] 1887198, 0x001CCBDE,  ...,  669251, 0x000A3643, 
     or      [04] 40

     Most of the codes are listed in the CODE Definitions Table in the
     TGC developer guide in
       \MindSet Development Tools\ThinkGear\mindset_communications_protocol.pdf

     The first number following the code is the relevant decimal value,
     except in the case of code 83 for EEG waves
  */
  {
    String codeStr = dataStr.substring(1, 3);
    // System.out.println("Code string: " + codeStr);

    String[] params = dataStr.substring(4).trim().split(",");
    for(int i=0; i < params.length; i++)
      params[i] = params[i].trim();

    if (codeStr.equals("02")) {
      if (!params[0].equals("0"))    // don't print normal signal data
        System.out.println("\n[" + eTime + " ms] Poor Signal: " + params[0]);
    }
    else if (codeStr.equals("04")) {
      System.out.println("\n[" + eTime + " ms] Attention: " + params[0]);
    } 
    else if (codeStr.equals("05")) {
      System.out.println("\n[" + eTime + " ms] Meditation: " + params[0]);
    } 
    else if (codeStr.equals("16")) {
      System.out.println("\n[" + eTime + " ms] Blink Strength: " + params[0]);
    } 
    else if (codeStr.equals("80")) {
      // format: 133, 0085, -2.219941
      if (showRaws)    // reduces output volume
        System.out.println("\n[" + eTime + " ms] Raw: " + params[0]);
    } 
    else if (codeStr.equals("83")) {
      System.out.println("\n[" + eTime + " ms] EEG");
      printEEGs(params);
    } 
    else
      System.out.println("\n[" + eTime + " ms] Unknown code: " + codeStr);

  }  // end of printPacket()



  private static void printEEGs(String[] params)
  /* The EEG data reports 8 different wave values, in both decimal and
     hexadecimal. For example:
              299209, 0x000490C9,  193287, 0x0002F307,   22743, 0x000058D7,   
              11035, 0x00002B1B,   21571, 0x00005443,    3502, 0x00000DAE,    
               3820, 0x00000EEC,   71882, 0x000118CA, 
     They appear to be for:
             delta, theta, low-alpha
             high-alpha, low-beta, high-beta,
             low-gamma, and mid-gamma
     The alpha and beta ranges seem to be swapped in the log data, but 
     I've left them in that order. 

     Only the decimals are printed here.
  */
  { System.out.println("  delta: " + params[0] + ";  theta: " + params[2]);
    System.out.println("  alpha (low): " + params[4] + " -- (high): " + params[6]);
    System.out.println("  beta (low): " + params[8] + " -- (high): " + params[10]);
    System.out.println("  gamma (low): " + params[12] + " -- (high): " + params[14]);
  }  // end of printEEGs()



  private static void printParams(String[] params)
  {
    System.out.print("[ ");
    for (String paramStr : params)
       System.out.print(paramStr + "  ");
    System.out.println("]");
  }  // end of printParams()

}  // end of ReadPacketsLog class
